﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpGroupUtils.h"
#include "IElementBasicInformation.h"
#include "IGroup.h"
#include "IInstance.h"
#include "IGenericElement.h"
#include "IModifyJoinRelationship.h"
#include "ILinkDocumentElement.h"
#include "ILinkElementProxy.h"
#include "IDocument.h"
#include "ICutRelationshipBehavior.h"
#include "IGeometryRelationshipComponent.h"
#include "IElementCut.h"
#include "IGroupType.h"
#include "IElementJoin.h"
#include "IElementOpening.h"
#include "IModelLine.h"
#include "IJoinRelationshipBehavior.h"
#include "IOpeningRelationshipBehavior.h"
#include "IAnnotationText.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

const std::wstring GbmpGroupUtils::GroupEditModeType = L"GroupEditModeType";
const std::wstring GbmpGroupUtils::GroupTypeEditModeType = L"GroupTypeEditModeType";

bool GbmpGroupUtils::IsAllowedElement(const IGroup* pOwnerGroup, const IElement* pElement)
{
    if (pElement == nullptr)
        return false;

    const IElementBasicInformation* pBasicInformation = pElement->GetBasicInformation();
    if (pBasicInformation == nullptr || pBasicInformation->GetIsTransient())
        return false;

    const IInstance* pInstance = quick_cast<const IInstance>(pElement);
    if (pInstance)
        return true;

    if (const IGenericElement* pGenericElement = quick_cast<const IGenericElement>(pElement))
    {
        // 轮廓族也会用到modelline，为了支持板、板洞这样的轮廓族，暂不支持modelline成组
        if (const IModelLine* pModelLine = quick_cast<const IModelLine>(pGenericElement->GetExternalObject()))
        {
            return false;
        }
        return true;
    }

    const IGroup* pGroup = quick_cast<const IGroup>(pElement);
    if (pGroup)
        return true;

    const ILinkDocumentElement* pLinkDocElement = quick_cast<const ILinkDocumentElement>(pElement);
    if (pLinkDocElement)
        return true;

    const ILinkElementProxy* pLinkElementProxy = quick_cast<const ILinkElementProxy>(pElement);
    if (pLinkElementProxy)
        return true;

    const IElementJoin* pElementJoin = quick_cast<const IElementJoin>(pElement);
    if (pElementJoin)
    {
        if (pOwnerGroup)
        {
            if (pOwnerGroup->IsElementInGroup(pElementJoin->GetAttachedId()) &&
                pOwnerGroup->IsElementInGroup(pElementJoin->GetAttachingId()))
                return true;
        }
        else
            return true;
        
    }

    const IElementCut* pElementCut = quick_cast<const IElementCut>(pElement);
    if (pElementCut)
        return true;

    const IElementOpening* pElementOpening = quick_cast<const IElementOpening>(pElement);
    if (pElementOpening)
        return true;

    const IAnnotationText* pAnnotationText = quick_cast<const IAnnotationText>(pElement);
    if (pAnnotationText)
        return true;

    return false;
}

void GbmpGroupUtils::SyncRelationships(IDocument* pDoc, IElement* pGroupOrGroupTypeElement, ElementId elementId, bool isAddOperation)
{
    IGroup* pGroup = quick_cast<IGroup>(pGroupOrGroupTypeElement);
    IGroupType* pGroupType = quick_cast<IGroupType>(pGroupOrGroupTypeElement);
    if (!pDoc || ( !pGroup && !pGroupType))
        return;

    IElement* pElement = pDoc->GetElement(elementId);
    if (!pElement)
        return;

    // 连接
    IGeometryRelationshipComponent* pGeometryCmpt = pElement->GetGeometryRelationshipComponent();
    if (pGeometryCmpt && pGeometryCmpt->GetJoinRelationship())
    {
        IJoinRelationshipBehavior* pJoinBehavior = pGeometryCmpt->GetJoinRelationship();
        FOR_EACH(joinId, pJoinBehavior->GetElementJoinIdsAtStart())
        {
            IElementJoin* pElementJoin = quick_cast<IElementJoin>(pDoc->GetElement(joinId));
            if (pElementJoin && pGroup && (
                (pGroup->IsElementInGroup(pElementJoin->GetAttachedId()) && pElementJoin->GetAttachingId() == elementId) ||
                (pGroup->IsElementInGroup(pElementJoin->GetAttachingId()) && pElementJoin->GetAttachedId() == elementId)))
            {
                if (isAddOperation)
                    pGroup->AddElement(joinId);
                else
                    pGroup->RemoveElement(joinId);
            }
            else if (pElementJoin && pGroupType)
            {
                const std::set<ElementId>& elementIdsInGroupType = pGroupType->GetElementIdsInGroupType();
                if ((elementIdsInGroupType.find(pElementJoin->GetAttachedId()) != elementIdsInGroupType.end() && elementId == pElementJoin->GetAttachingId()) ||
                    (elementIdsInGroupType.find(pElementJoin->GetAttachingId()) != elementIdsInGroupType.end() && elementId == pElementJoin->GetAttachedId()))
                {
                    if (isAddOperation)
                        pGroupType->AddElement(joinId);
                    else
                        pGroupType->RemoveElement(joinId);
                }
            }
        }
        FOR_EACH(joinId, pJoinBehavior->GetElementJoinIdsAtMiddle())
        {
            IElementJoin* pElementJoin = quick_cast<IElementJoin>(pDoc->GetElement(joinId));
            if (pElementJoin && pGroup &&  (
                (pGroup->IsElementInGroup(pElementJoin->GetAttachedId()) && pElementJoin->GetAttachingId() == elementId) ||
                (pGroup->IsElementInGroup(pElementJoin->GetAttachingId()) && pElementJoin->GetAttachedId() == elementId)))
            {
                if (isAddOperation)
                    pGroup->AddElement(joinId);
                else
                    pGroup->RemoveElement(joinId);
            }
            else if (pElementJoin && pGroupType)
            {
                const std::set<ElementId>& elementIdsInGroupType = pGroupType->GetElementIdsInGroupType();
                if ((elementIdsInGroupType.find(pElementJoin->GetAttachedId()) != elementIdsInGroupType.end() && elementId == pElementJoin->GetAttachingId()) ||
                    (elementIdsInGroupType.find(pElementJoin->GetAttachingId()) != elementIdsInGroupType.end() && elementId == pElementJoin->GetAttachedId()))
                {
                    if (isAddOperation)
                        pGroupType->AddElement(joinId);
                    else
                        pGroupType->RemoveElement(joinId);
                }
            }
        }
        FOR_EACH(joinId, pJoinBehavior->GetElementJoinIdsAtEnd())
        {
            IElementJoin* pElementJoin = quick_cast<IElementJoin>(pDoc->GetElement(joinId));
            if (pElementJoin && pGroup && (
                (pGroup->IsElementInGroup(pElementJoin->GetAttachedId()) && pElementJoin->GetAttachingId() == elementId) ||
                (pGroup->IsElementInGroup(pElementJoin->GetAttachingId()) && pElementJoin->GetAttachedId() == elementId)))
            {
                if (isAddOperation)
                    pGroup->AddElement(joinId);
                else
                    pGroup->RemoveElement(joinId);
            }
            else if (pElementJoin && pGroupType)
            {
                const std::set<ElementId>& elementIdsInGroupType = pGroupType->GetElementIdsInGroupType();
                if ((elementIdsInGroupType.find(pElementJoin->GetAttachedId()) != elementIdsInGroupType.end() && elementId == pElementJoin->GetAttachingId()) ||
                    (elementIdsInGroupType.find(pElementJoin->GetAttachingId()) != elementIdsInGroupType.end() && elementId == pElementJoin->GetAttachedId()))
                {
                    if (isAddOperation)
                        pGroupType->AddElement(joinId);
                    else
                        pGroupType->RemoveElement(joinId);
                }
            }
        }
    }

    // 扣减
    if (pGeometryCmpt && pGeometryCmpt->GetCutRelationship())
    {
        ICutRelationshipBehavior* pCutBehavior = pGeometryCmpt->GetCutRelationship();
        FOR_EACH(cutId, pCutBehavior->GetElementCutIds())
        {
            IElementCut* pElementCut = quick_cast<IElementCut>(pDoc->GetElement(cutId));
            if (pElementCut && pGroup && (
                (pGroup->IsElementInGroup(pElementCut->GetCutteeId()) && pElementCut->GetCutterId() == elementId) ||
                (pGroup->IsElementInGroup(pElementCut->GetCutterId()) && pElementCut->GetCutteeId() == elementId)))
            {
                if (isAddOperation)
                    pGroup->AddElement(cutId);
                else
                    pGroup->RemoveElement(cutId);
            }
            else if (pElementCut && pGroupType)
            {
                const std::set<ElementId>& elementIdsInGroupType = pGroupType->GetElementIdsInGroupType();
                if ((elementIdsInGroupType.find(pElementCut->GetCutteeId()) != elementIdsInGroupType.end() && elementId == pElementCut->GetCutterId()) ||
                    (elementIdsInGroupType.find(pElementCut->GetCutterId()) != elementIdsInGroupType.end() && elementId == pElementCut->GetCutteeId()))
                {
                    if (isAddOperation)
                        pGroupType->AddElement(cutId);
                    else
                        pGroupType->RemoveElement(cutId);
                }
            }
        }
    }

    // 打洞
    if (pGeometryCmpt && pGeometryCmpt->GetOpeningRelationship())
    {
        IOpeningRelationshipBehavior* pOpeningBehavior = pGeometryCmpt->GetOpeningRelationship();
        FOR_EACH(openingId, pOpeningBehavior->GetElementOpeningIds())
        {
            IElementOpening* pElementOpening = quick_cast<IElementOpening>(pDoc->GetElement(openingId));
            if (pElementOpening && pGroup && (
                (pGroup->IsElementInGroup(pElementOpening->GetOpenedId()) && pElementOpening->GetOpeningId() == elementId) ||
                (pGroup->IsElementInGroup(pElementOpening->GetOpeningId()) && pElementOpening->GetOpenedId() == elementId)))
            {
                if (isAddOperation)
                    pGroup->AddElement(openingId);
                else
                    pGroup->RemoveElement(openingId);
            }
            else if (pElementOpening && pGroupType)
            {
                const std::set<ElementId>& elementIdsInGroupType = pGroupType->GetElementIdsInGroupType();
                if ((elementIdsInGroupType.find(pElementOpening->GetOpenedId()) != elementIdsInGroupType.end() && elementId == pElementOpening->GetOpeningId()) ||
                    (elementIdsInGroupType.find(pElementOpening->GetOpeningId()) != elementIdsInGroupType.end() && elementId == pElementOpening->GetOpenedId()))
                {
                    if (isAddOperation)
                        pGroupType->AddElement(openingId);
                    else
                        pGroupType->RemoveElement(openingId);
                }
            }
        }
    }
}

void GbmpGroupUtils::UpdateGroupElementRelationship(IDocument* pDocument, const ElementId& elementId)
{
    UpdateGroupElementJoinRelationship(pDocument, elementId);
}

void GbmpGroupUtils::PreUpdateGroupElementCutRelationship(IDocument* pDocument, const ElementId& elementId, std::map<ElementId, ElementId>& cutterAndCutteeMap)
{
    IElement* pSourceElement = pDocument->GetElement(elementId);
    if (pSourceElement == nullptr)
    {
        return;
    }
    const IGeometryRelationshipComponent* pGeometryRepCpt = pSourceElement->GetGeometryRelationshipComponent();

    if (pGeometryRepCpt)
    {
        const ICutRelationshipBehavior* pCutRelationshipBehavior = pGeometryRepCpt->GetCutRelationship();
        std::vector<ElementId> cutIds;
        if (pCutRelationshipBehavior != nullptr)
        {
            cutIds = pCutRelationshipBehavior->GetElementCutIds();
            FOR_EACH(id, cutIds)
            {
                IElementCut* pCut = quick_cast<IElementCut>(pDocument->GetElement(id));
                if (pCut == nullptr)
                {
                    continue;
                }
                const ElementId sourceCuttingId = pCut->GetCutterId();
                const ElementId sourceCuttedId = pCut->GetCutteeId();
                cutterAndCutteeMap.insert(std::pair<ElementId, ElementId>(sourceCuttingId, sourceCuttedId));
            }
        }
    }
}

void GbmpGroupUtils::PostUpdateTypeElementCutRelationship(IDocument* pDocument, const std::map<ElementId, ElementId>& cutterAndCutteeMap)
{
    for (const auto& item : cutterAndCutteeMap)
    {
        const ElementId cutter = item.first;
        const ElementId cuttee = item.second;

        IElement* pElementInGroup = pDocument->GetElement(cutter);
        if (pElementInGroup == nullptr)
        {
            continue;
        }
        ElementId groupId = pElementInGroup->GetGroupId();

        const IGroup* pGroup = quick_cast<const IGroup>(pDocument->GetElement(groupId));
        if (pGroup == nullptr)
            continue;

        const std::map<ElementId, ElementId>& typeToGroupIdMap = pGroup->GetTypeToGroupIdMap();
        auto iter = typeToGroupIdMap.begin();

        ElementId newCutter = ElementId::InvalidID;
        ElementId newCuttee = ElementId::InvalidID;
        for (const auto& item : typeToGroupIdMap)
        {
            if (item.second == cutter)
            {
                newCutter = item.first;
            }
            if (item.second == cuttee)
            {
                newCuttee = item.first;
            }

            if (newCutter != ElementId::InvalidID && newCuttee != ElementId::InvalidID)
            {
                IElementCut::Create(pDocument, newCutter, newCuttee,false,false);
            }
        }
    }
}

void GbmpGroupUtils::PostUpdateGroupElementCutRelationship(IDocument* pDocument, const ElementId& elementId)
{
    IElement* pTargetElement = pDocument->GetElement(elementId);
    if (pTargetElement == nullptr)
    {
        return;
    }
    ElementId groupId = pTargetElement->GetGroupId();

    IGroup* pGroup = quick_cast<IGroup>(pDocument->GetElement(groupId));
    if (pGroup == nullptr)
    {
        return;
    }

    const std::map<ElementId, ElementId>& typeToGroupIdMap = pGroup->GetTypeToGroupIdMap();

     for (auto &it : typeToGroupIdMap)
     {
         IElement* pTypeElement = pDocument->GetElement(it.first);
         if (!pTypeElement)
         {
             continue;
         }
         const IGeometryRelationshipComponent* pGeometryRepCpt = pTypeElement->GetGeometryRelationshipComponent();
         if (pGeometryRepCpt)
         {
             const ICutRelationshipBehavior* pCutRelationshipBehavior = pGeometryRepCpt->GetCutRelationship();
             if (pCutRelationshipBehavior != nullptr)
             {
                 std::vector<ElementId> cutIds = pCutRelationshipBehavior->GetElementCutIds();
                 FOR_EACH(id, cutIds)
                 {
                     IElementCut* pCut = quick_cast<IElementCut>(pDocument->GetElement(id));
                     if (pCut == nullptr)
                     {
                         continue;
                     }
                     const ElementId sourceCuttingId = pCut->GetCutterId();
                     const ElementId sourceCuttedId = pCut->GetCutteeId();

                     if (it.first == sourceCuttingId)
                     {
                         //如果当前ElementId是扣减Element，判断被扣减的Element是否在组内
                         auto iter = typeToGroupIdMap.find(sourceCuttedId);
                         if (iter != typeToGroupIdMap.end())
                         {
                             //组内源Element存在扣减关系，新创建组内对应的Element也创建扣减关系
                             IElementCut::Create(pDocument, it.second, iter->second,false,false);
                         }
                     }
                     else if (it.first == sourceCuttedId)
                     {
                         //如果当前ElementId是被扣减Element，判断扣减的Element是否在组内
                         auto iter = typeToGroupIdMap.find(sourceCuttingId);
                         if (iter != typeToGroupIdMap.end())
                         {
                             IElementCut::Create(pDocument, iter->second, it.second,false,false);
                         }
                     }
                 }
             }
         }
     }
}

void GbmpGroupUtils::UpdateGroupElementCutRelationship(IDocument* pDocument, const std::map<ElementId, ElementId>& sourceAndTargetMap)
{
    for (auto &it: sourceAndTargetMap)
    {
        IElement* pSourceElement = pDocument->GetElement(it.first);
        if (!pSourceElement)
        {
            continue;
        }

        const IGeometryRelationshipComponent* pGeometryRepCpt = pSourceElement->GetGeometryRelationshipComponent();
        if (pGeometryRepCpt)
        {
            const ICutRelationshipBehavior* pCutRelationshipBehavior = pGeometryRepCpt->GetCutRelationship();
            if (pCutRelationshipBehavior != nullptr)
            {
                std::vector<ElementId> cutIds = pCutRelationshipBehavior->GetElementCutIds();
                FOR_EACH(id, cutIds)
                {
                    IElementCut* pCut = quick_cast<IElementCut>(pDocument->GetElement(id));
                    if (pCut == nullptr)
                    {
                        continue;
                    }
                    const ElementId sourceCuttingId = pCut->GetCutterId();
                    const ElementId sourceCuttedId = pCut->GetCutteeId();

                    if (it.first == sourceCuttingId)
                    {
                        //如果当前ElementId是扣减Element，判断被扣减的Element是否在组内
                        auto iter = sourceAndTargetMap.find(sourceCuttedId);
                        if (iter != sourceAndTargetMap.end())
                        {
                            //组内源Element存在扣减关系，新创建组内对应的Element也创建扣减关系
                            IElementCut::Create(pDocument, it.second, iter->second);
                        }
                    }
                    else if (it.first == sourceCuttedId)
                    {
                        //如果当前ElementId是被扣减Element，判断扣减的Element是否在组内
                        auto iter = sourceAndTargetMap.find(sourceCuttingId);
                        if (iter != sourceAndTargetMap.end())
                        {
                            IElementCut::Create(pDocument, iter->second, it.second);
                        }
                    }
                }
            }
        }
    }
}

void GbmpGroupUtils::UpdateGroupElementJoinRelationship(IDocument* pDocument, const ElementId& elementId)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"无效的文档",L"GDMPLab",L"2024-03-30");

    const IModifyJoinRelationship *pModifyJoinRelationship = IModifyJoinRelationship::GetModifyJoinRelationship();
    if (pModifyJoinRelationship)
    {
        pModifyJoinRelationship->DeleteMoveElementJoins(pDocument, elementId);
        pModifyJoinRelationship->BuildJoinInMoveElements(pDocument, elementId);
    }
}
